package com.zhuqi.filter;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zhuqi
 * @version 1.0
 * @date 2021/3/26 17:34
 * 解析 JWT 和 验证二维码的 过滤器的
 * spring-security-mysql-thymeleaf --> 中实现了二维码
 */
@Component
public class JwtAndCodeFilter extends OncePerRequestFilter {

    /**
     * 解析 JWT
     * 这个过滤器还可以配合 验证码使用
     *
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 获取请求类型 如果为登录就放过
        String path = request.getRequestURI();
        String method = request.getMethod();
        // 如果是登录请求就放过
        if ("/doLogin".equalsIgnoreCase(path) && "post".equalsIgnoreCase(method)) {
            filterChain.doFilter(request, response);
            return;
        }
        // 解析前端请求的中携带的 token 这里模拟前端是放在请求头中传递过来的的情况
        String jwt = request.getHeader("token");
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("haha")).build();
        // 解析 JWT 可能会失败我们 try 起来
        DecodedJWT verify = null;
        try {
            verify = jwtVerifier.verify(jwt);
        } catch (JWTVerificationException e) {
            // 解析失败的情况
            // 这里可以记录日志或者请求转发到异常处理的 controller 让其报错进行异常捕获处理 这里就简单处理处理返回给前端
            HashMap<String, Object> map = new HashMap<>(8);
            map.put("code", 403);
            map.put("msg", "token有误");
            response.setContentType("application/json;charset=utf-8");
            PrintWriter writer = response.getWriter();
            try {
                writer.write(new ObjectMapper().writeValueAsString(map));
            } catch (Exception exception) {
                writer.flush();
                writer.close();
                return;
            }
        }
        // 解析成功的情况 获取负载里面的信息
        Map<String, Claim> claims = verify.getClaims();
        String username = claims.get("username").asString();
        List<String> auths = claims.get("auths").asList(String.class);
        // 转换权限数据 Collection<? extends GrantedAuthority> authoritie
        ArrayList<GrantedAuthority> authorities = new ArrayList<>(auths.size() * 2);
        auths.forEach(auth -> authorities.add(new SimpleGrantedAuthority(auth)));
        // 放入 安全上下文中
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
        // 将细节 放入令牌中如客户端 ip之类的
        authenticationToken.setDetails(new WebAuthenticationDetails(request));
        // 将令牌放入 安全上下文容器中
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        filterChain.doFilter(request, response);
        return;
    }
}
